package presenter;

import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;

import data.*;
import GUI.*;
import network.*;

/**
 * 
 * @author Benjamin GOUIT et David SOLO RABRIMANANA
 *
 */
public class ChatPresenter {

	private Data data;
	private ChatGUI gui;
	private ViewNI ni;
	private int compteurID = 0;
	
	/**
	 * Constructeur
	 */
	public ChatPresenter () {
		ni = new ViewNI (this);
		gui = new ChatGUI (this);
	}
	
	/**
	 * Constructeur
	 * @param gui
	 * @param ni
	 */
	public ChatPresenter (ChatGUI gui, ViewNI ni) {
		this.ni = ni;
		this.gui = gui;
		
		ni.setChatPres(this);
		gui.setChatPres(this);
	}
	
	/**
	 * Création de data et demande au NI d'envoyer un "Hello"
	 * @param username
	 */
	public void performConnect (String username) {
		
		try {
			data = new Data (username, (InetAddress.getLocalHost()).toString());
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		// Ajout du User "broadcast" pour le salon principal
		data.addNewUser("broadcast", "255.255.255.255");
		ni.sendHello (username);
	}

	/**
	 * Demande au NI d'envoyer un GoodBye et de fermer le socket UDP
	 */
	public void performDisconnect () {
		ni.sendGoodbye (data.getLocalName());
		ni.closeSocket ();
	}
	
	/**
	 * Récupération des adresses des destinataires dans data et envoi du text par NI aux destinataires
	 * @param usernames
	 * @param msg
	 */
	public void textToSend (String[] usernames, String msg) {
		String[] addrs = new String[usernames.length];
		
		for (int i = 0; i < usernames.length; i++)
			addrs[i] = data.getAddr(usernames[i]);
		
		ni.sendText (addrs, msg);
	}
	
	/**
	 * Reception d'un nouvel utilisateur
	 * @param username
	 * @param addr
	 */
	public void performAddNewUser (String username, String addr) {
		newUserReceived (username, addr);
	}
	
	/**
	 * Ajout d'un nouvelle utilisateur et envoi d'un Hello Reply lors d'une reception d'un Hello par NI
	 * @param username
	 * @param addr
	 */
	public void helloReceived (String username, String addr) {
		newUserReceived (username, addr);
		ni.sendHelloReply(data.getLocalName(), addr);
	}
	
	/**
	 * Demande au NI d'envoyer un Hello Reply lorsqu'on ne connait pas l'emetteur du message
	 * @param addr
	 */
	public void loginUnknown (String addr) {
		ni.sendHelloReply (data.getLocalName(), addr);
	}
	
	/**
	 * Ajout d'un nouvel utilisateur dans data et informe le GUI
	 * @param username
	 * @param addr
	 */
	private void newUserReceived (String username, String addr) {
		if (data.addNewUser(username, addr))
			gui.newUserConnected(data.getUser(addr).toString());
	}

	/**
	 * Informe le GUI de la déconnection d'un utilisateur et suppression de l'utilisateur dans data
	 * @param addr
	 */
	public void goodByeReceived(String addr) {
		gui.userDisconnect (data.removeUser(addr));
	}

	/**
	 * Lors de la reception d'un message texte
	 * Recupération du nom de l'emetteur
	 * demande au NI d'envoyer un AskLogin si l'emetteur est inconnu et création d'un thread pour recevoir le message
	 * Sinon reception du message et demande au GUI de l'afficher
	 * @param message
	 * @param conversMembers
	 * @param broadcast
	 */
	// conversMembers[0] est l'expéditeur
	public void textReceived(String message, String[] conversMembers, boolean broadcast) {
		User[] Umembers = new User[conversMembers.length];
		boolean memberUnknown = false;
		for (int i = 0; i < conversMembers.length; i++) {
			Umembers[i] = data.getUser(conversMembers[i]);
			 if (Umembers[i] == null) {
				 memberUnknown = true;
				 ni.askLogin (conversMembers[i]);
				 System.out.println("unknown : "+conversMembers[i]);
			 }
		}
		if (memberUnknown) {
			System.out.println("text received - at least one user unknown");
			
			(new Thread(new TextWaiter (message, conversMembers, broadcast))).start();
		}
		else {
			System.out.println("text received - users known");
			String[] members = new String[Umembers.length];
			for (int i = 0; i < Umembers.length; i++) {
				members[i] = new String(Umembers[i].toString());
			}
			gui.textToPrint(message, members, broadcast);
		}
	}

	/**
	 * Ajout d'un File dans data et demande d'envoi d'un PropFile par NI + Gestion de l'ID
	 * @param username
	 * @param file
	 */
	public void fileToSend(String username, File file) {
		String addr = data.getAddr(username);
		
		data.addFileToSend (file, compteurID, addr);
		ni.propFile(addr, file.getName(), file.length(), compteurID);
		
		compteurID++;
	}
	
	/**
	 * Si le recepteur accepte le fichier
	 * Suppression du fichier de data
	 * Indication au GUI de l'envoi du fichier
	 * Creation d'un trhread pour envoyer le fichier
	 * Si le recepteur ne veut pas recevoir le fichier maintenant
	 * Indication au GUI d'attendre
	 * Si le recepteur refuse le fichier
	 * Suppression du fichier de data
	 * Indication au GUI du refus
	 * @param addr
	 * @param fileId
	 * @param accept
	 * @param now
	 */
	public void acceptFile(String addr, int fileId, boolean accept, boolean now) {
		File fileToSend = data.getFileToSend (fileId, addr);
		
		if (accept) {
			if (now) {
				data.removeFileToSend(fileId, addr);
				
				gui.fileIsSending (data.getUser(addr).toString(), fileToSend.getName());
				(new Thread(new FileSender (addr, fileToSend, fileId))).start();
			}
			else {
				gui.fileIsWaiting (data.getUser(addr).toString(), fileToSend.getName());
			}
		}
		else {
			data.removeFileToSend(fileId, addr);
			
			gui.fileRefused (data.getUser(addr).toString(), fileToSend.getName());
		}
	}

	/**
	 * Indication au GUI de la reception d'un PropFile par NI
	 * Demande a NI d'envoyer un AcceptFile selon la reponse de l'utisateur
	 * Indication au GUI de la reception du fichier si le fichier a bien été reçu
	 * @param addr
	 * @param filename
	 * @param filesize
	 * @param file_id
	 */
	public void propFileReceived(String addr, String filename, long filesize, int file_id) {
		File file = gui.propFileReceived (data.getUser(addr).toString(), filename, filesize);
		  
		if (file == null)
			ni.sendAcceptFile(file, addr, filesize, file_id, false, false);
		else {
			ni.sendAcceptFile(file, addr, filesize, file_id, true, true);
			
			gui.fileReceived (file.getName(), file.getAbsolutePath());
		}
	}
	
	/**
	 * 
	 * @author benjamin et david
	 * Thread pour la reception de texte si l'emetteur est inconnu
	 *
	 */
	private class TextWaiter implements Runnable {
		
		private String[] conversMembers;
		private String message;
		private boolean broadcast;
		
		/**
		 * Constructeur
		 * @param message
		 * @param conversMembers
		 * @param broadcast
		 */
		public TextWaiter (String message, String[] conversMembers, boolean broadcast) {
			this.conversMembers = conversMembers;
			this.message = message;
			this.broadcast = broadcast;
		}
		
		/**
		 * Lanceur du thread d'attente
		 */
		@Override
		public void run() {
			boolean memberUnknown = true;
			int i = 0;
			//Wait for 5 seconds maximum
			while ((memberUnknown) && (i < 50)) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				memberUnknown = false;
				for (int n = 0; n < conversMembers.length; n++) {
					User u = data.getUser(conversMembers[i]);
					if (u == null) {
						 memberUnknown = true;
					 }
				}
				i++;
			}

			textReceived(message, conversMembers, broadcast);
		}
	}
	
	/**
	 * 
	 * @author benjamin david
	 * Thread d'envoi de fichier
	 *
	 */
	private class FileSender implements Runnable {
		private String addr;
		private File fileToSend;
		private int fileId;
		
		/**
		 * Constructeur
		 * @param addr
		 * @param fileToSend
		 * @param fileId
		 */
		public FileSender (String addr, File fileToSend, int fileId) {
			this.addr = addr;
			this.fileToSend = fileToSend;
			this.fileId = fileId;
		}

		/**
		 * lanceur du thread
		 */
		public void run() {
			ni.sendFile (addr, fileToSend, fileId);
			
			gui.fileSent(data.getUser(addr).toString(), fileToSend.getName());
		}
	}
}
